package hibernatemock;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.validation.constraints.Null;

import lombok.Getter;
import lombok.Setter;

public class SetupBuilder {

	@Setter @Getter private Set<Setup> setups;
	@Setter private InstanceFactory instanceFactory;

	public SetupBuilder when(Class<?>... classes) {
		List<Class<?>> notAnnotations = new ArrayList<Class<?>>();
		List<Class<?>> annotations = new ArrayList<Class<?>>();
		for (Class<?> classTemp : classes) {
			if (Annotation.class.isAssignableFrom(classTemp))
				annotations.add(classTemp);
			else
				notAnnotations.add(classTemp);
		}

		if (annotations.size() > 1)
			throw new RuntimeException("You can setup only one annotation at a time.");

		for (Class<?> noAnnotationClass : notAnnotations) {
			if (annotations.size() == 0) {
				Setup setup = new Setup(noAnnotationClass);
				setups.remove(setup);
				setups.add(setup);
			}
			for (Class<?> annotationClass : annotations) {
				Setup setup = new Setup(noAnnotationClass, annotationClass);
				setups.remove(setup);
				setups.add(setup);
			}
		}
		return this;
	}

	public void deliver(Object instance) {
		realDeliver(instance);
	}

	public void deliverNull() {
		realDeliver(null);
	}

	
	public void deliverWith(Object... fieldsAndValues) {

		for (Setup setup : setups) {
			if (!setup.hasInstance() && setup.getClazzAnnotation() != Null.class) {
				Object instance = instanceFactory.newInstance(setup.getClazzField());
				for (int i = 0; i < fieldsAndValues.length; i = i + 2) {
					try {
						Field field = setup.getClazzField().getDeclaredField(fieldsAndValues[i].toString());
						field.setAccessible(true);
						field.set(instance, fieldsAndValues[i + 1]);
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
				}
				setup.setInstance(instance);
			}
		}
	}

	public void deliver(FieldHandler fieldHandler) {
		realDeliver(fieldHandler);
	}

	private void realDeliver(Object instance) {
		for (Setup setup : setups)
			if (!setup.hasInstance() && setup.getClazzAnnotation() != Null.class)
				setup.setInstance(instance);
	}

}
